#include "stdafx.h"

using namespace std;

/////////////////////////////////////////////////////////////////////////////////
//definitions
ofstream out;
struct trampoline{char save[5], jmp[5];};

//hooks
typedef HRESULT (WINAPI *tEndScene)(LPDIRECT3DDEVICE9 pDevice);
tEndScene oEndScene = NULL; //original
tEndScene t_EndScene = NULL; //trampoline

typedef HRESULT (WINAPI *tReset)(LPDIRECT3DDEVICE9 pDevice, D3DPRESENT_PARAMETERS *params);
tReset oReset = NULL; //original
tReset t_Reset = NULL; //trampoline

LPDIRECT3DDEVICE9 npDevice = NULL;
PDWORD pTable = NULL;
DWORD trFlag[2];
HANDLE tHandle;

LPD3DXFONT      g_pFont = NULL; //D3D Font
LPD3DXLINE      g_pLine = NULL; //D3D Line
RECT rect;

/////////////////////////////////////////////////////////////////////////////
//functions
void installHook(), uninstallHook();


LRESULT CALLBACK MsgProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){return DefWindowProc(hwnd, uMsg, wParam, lParam);}


HRESULT hkEndScene(LPDIRECT3DDEVICE9 pDevice){
	HRESULT result;
	out << "EndScene called" << endl;
	if(g_pFont == NULL){
		rect.top = 300;
		rect.right = 400;
		rect.bottom = 400;
		rect.left = 0;
		//D3DXFONT_DESC FontDesc = {24, 0, 400, 0, false, DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_PITCH, "Arial"};
		//D3DXCreateFontIndirectA(pDevice,&FontDesc,&g_pFont);
		D3DXCreateFontA(pDevice, 24, 10, FW_BOLD, 1, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, 
		CLIP_DEFAULT_PRECIS, DEFAULT_PITCH, "Arial", &g_pFont); //Create fonts
	} 
	g_pFont->DrawText(NULL, "das is ein test", strlen("das is ein test"), &rect, DT_CENTER, 0xffff0000);
	result = t_EndScene(pDevice);
	g_pFont->Release();
	g_pFont = NULL;
	return result;
}


HRESULT hkReset(LPDIRECT3DDEVICE9 pDevice, D3DPRESENT_PARAMETERS *params){
	HRESULT result;
	out << "Reset  called" << endl;
	if(g_pFont){
		g_pFont->Release();
		g_pFont = NULL;
	}
	result = t_Reset(pDevice, params);
	return result;
}


void hookEndScene(PDWORD vTable){
	DWORD flag;
	struct trampoline *trampoline;
	char *ptr;
	trampoline = (struct trampoline*)calloc(1, sizeof(struct trampoline));
	memset(trampoline, 0x90, sizeof(struct trampoline));
	try{
		if(VirtualProtect((PDWORD)vTable[ES], sizeof(DWORD)*2, PAGE_READWRITE, &flag) == FALSE)
			throw 1;
		if(VirtualProtect(trampoline, sizeof(struct trampoline), PAGE_EXECUTE_READWRITE, &trFlag[0]) == FALSE)
			throw 2;
		ptr = (char*)vTable[ES];
		memcpy(trampoline->save, ptr, 5);
		*(PDWORD)&oEndScene = vTable[ES];
		ptr[0] = 0xe9;
		*(PDWORD)&ptr[1] = (DWORD)hkEndScene - (DWORD)vTable[ES] - 5;
		trampoline->jmp[0] = 0xe9;
		*(PDWORD)&trampoline->jmp[1] = (DWORD)oEndScene - (DWORD)trampoline->jmp;
		*(PDWORD)&t_EndScene = (DWORD)trampoline;
		if(VirtualProtect((PDWORD)vTable[ES], sizeof(DWORD)*2, flag, &flag) == FALSE)
			throw 3;
		out << "EndScene hooked" << endl;
	}
	catch(int code){
		switch(code){
		case 1: break;
		case 2: break;
		case 3: break;
		default: break;
		}
	}
}


void hookReset(PDWORD vTable){
	DWORD flag;
	struct trampoline *trampoline;
	char *ptr;
	try{
		trampoline = (struct trampoline*)calloc(1, sizeof(struct trampoline));
		memset(trampoline, 0x90, sizeof(struct trampoline));
		if(!VirtualProtect((PDWORD)vTable[RES], sizeof(struct trampoline), PAGE_READWRITE, &flag))
			throw 1;
		if(!VirtualProtect(trampoline, sizeof(struct trampoline), PAGE_EXECUTE_READWRITE, &trFlag[1]))
			throw 2;
		ptr = (char*)vTable[RES];
		*(PDWORD)&oReset = vTable[RES];
		memcpy(trampoline->save, ptr, 5);
		ptr[0] = 0xe9;
		*(PDWORD)&ptr[1] = (DWORD)hkReset - vTable[RES] - 5;
		trampoline->jmp[0] = 0xe9;
		*(PDWORD)&trampoline->jmp[1] = (DWORD)oReset - (DWORD)trampoline->jmp;
		*(PDWORD)&t_Reset = (DWORD)trampoline;
		if(!VirtualProtect((PDWORD)vTable[RES], sizeof(struct trampoline), flag, &flag))
			throw 3;
		out << "Reset hooked" << endl;
	}catch(int code){
		switch(code){
		case 1: break;
		case 2: break;
		case 3: break;
		default: break;
		}
	}
}


void installHook(PDWORD vTable){
	hookEndScene(vTable);
	//hookReset(vTable);
}


void uninstallHook(){
	struct trampoline *trampoline, *orig;
	DWORD flag;
	trampoline = (struct trampoline*) t_EndScene;
	try{
		orig = (struct trampoline*) oEndScene;
		if(!VirtualProtect(orig, sizeof(struct trampoline), PAGE_READWRITE, &flag))
			throw 1;
		memcpy(orig->save, trampoline->save, 5);
		if(!VirtualProtect(orig, sizeof(struct trampoline), flag, &flag))
			throw 2;
		if(!VirtualProtect(trampoline, sizeof(struct trampoline), trFlag[0], &trFlag[0]))
			throw 3;
		free(trampoline);
	}
	catch(int code){
		switch(code){
		case 1: break;
		case 2: break;
		case 3: break;
		default: break;
		}
	}
}


PDWORD DX_Init(){
	PDWORD vTable;
	WNDCLASSEXA wc = {sizeof(WNDCLASSEX),CS_CLASSDC,MsgProc,0L,0L,GetModuleHandleA(NULL),NULL,NULL,NULL,NULL,"DX",NULL};
    RegisterClassExA(&wc);
    HWND hWnd = CreateWindowA("DX",NULL,WS_OVERLAPPEDWINDOW,100,100,300,300,GetDesktopWindow(),NULL,wc.hInstance,NULL);
    LPDIRECT3D9 pD3D = Direct3DCreate9( D3D_SDK_VERSION );
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    LPDIRECT3DDEVICE9 pd3dDevice;
	//pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,&pd3dDevice);
	pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_NULLREF,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,&pd3dDevice);
	vTable = (PDWORD)*(PDWORD)pd3dDevice;
	DestroyWindow(hWnd);
	return vTable;
}


DWORD vTableHook(PDWORD *vTable, PDWORD hook, int index){
	DWORD flag, *orig = 0;
	if(VirtualProtect((void*)(*vTable+4*index), 4, PAGE_READWRITE, &flag)){
		orig = (PDWORD) (*vTable)[ES];
		(*vTable)[ES] = (DWORD)hook;
		VirtualProtect((void*)(*vTable+4*index), 4, flag, &flag);
	}
	return (DWORD)orig;
}


DWORD WINAPI threadRoutine(LPVOID args){
	PDWORD vTable;
	out << "initializing HOOK" << endl;
	vTable = DX_Init();
	installHook(vTable);
	return 0;
}


BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD reason, LPVOID reserved){
	char path[MAX_PATH];
	time_t local;
	struct tm ref = {0};
	if(reason == DLL_PROCESS_ATTACH){
		GetEnvironmentVariable("APPDATA", path, MAX_PATH);
		strcat_s(path, MAX_PATH, STD_DIR);
		out = ofstream(path);
		local = time(NULL);
		localtime_s(&ref, &local);
		out << ref.tm_mday << "." << ref.tm_mon << "." << ref.tm_year + 1900 << "--" 
			<< ref.tm_hour << ":" << ref.tm_min << ":" << ref.tm_sec << endl;
		tHandle = CreateThread(NULL, NULL, threadRoutine, NULL, 0, NULL);
	}
	else if(reason == DLL_PROCESS_DETACH){
		TerminateThread(tHandle, 1);
		out.close();
	}
	return TRUE;
}